cmake_minimum_required(VERSION 3.9.4)
project(riscv CXX)

option(NATIVE      "Enable all host CPU features" OFF)
option(LTO         "Enable interprocedural optimizations" OFF)
option(SANITIZE    "Enable sanitizers" OFF)
option(TSAN        "Enable thread sanitizer" OFF)
option(BOLT        "Enable BOLT" OFF)
option(GPROF       "Enable profiling with gprof" OFF)
option(PERF        "Enable perf-friendly compiler flags" OFF)
option(LINKER_GC   "Enable linker section garbage collection" OFF)
option(STATIC_BUILD "Build this CLI program statically" OFF)

option(NEWLIB_EMULATOR "Build the Newlib variant (fewer system calls)" OFF)
option(MICRO_EMULATOR  "Build the special micro emulator (custom system calls)" OFF)

set(SOURCES
	src/main.cpp
)
if (EMBED_FILES)
	message(STATUS "Embedding code file ${EMBED_FILE}")
	set(SOURCES ${SOURCES} ${EMBED_FILES})
	# Disable warnings about constants changing signedness
	if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
		set_source_files_properties(${EMBED_FILES} PROPERTIES COMPILE_FLAGS "-Wno-constant-conversion")
	endif()
endif()

if (NATIVE)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
endif()
if (SANITIZE)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined")
elseif (TSAN)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread,undefined")
endif()
if (GPROF)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -g -fno-inline -fno-omit-frame-pointer")
endif()
if (PERF)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4 -static -fno-omit-frame-pointer")
endif()
if (BOLT)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4")
	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--emit-relocs")
endif()

# GC-sections
if (LINKER_GC)
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-gc-sections")
endif()

if (LTO)
	if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=full")
		if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.0)
			set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld-19")
		elseif (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 18.0)
			set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld-18")
		elseif (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 17.0)
			set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld-17")
		elseif (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 16.0)
			set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld-16")
		else()
			set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld")
		endif()
	else()
		include(CheckIPOSupported)
		check_ipo_supported(RESULT supported OUTPUT error)
		if (supported)
			message(STATUS "IPO / LTO enabled")
			set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
		else()
			message(STATUS "IPO / LTO not supported: <${error}>")
		endif()
	endif()
endif()

include(CheckFunctionExists)
check_function_exists("getopt_long" HAVE_GETOPT_LONG)

add_subdirectory(../lib lib)

if (RISCV_BINARY_TRANSLATION)
	find_package(Threads REQUIRED)
endif()

function (add_emulator NAME)
	add_executable(${NAME} ${SOURCES})
	target_link_libraries(${NAME} riscv)
	message(STATUS "ARGN is ${ARGN}")
	target_compile_definitions(${NAME} PRIVATE ${ARGN} HAVE_GETOPT_LONG=${HAVE_GETOPT_LONG})
	if (STATIC_BUILD)
		target_link_libraries(${NAME} -static)
	endif()
	if (RISCV_BINARY_TRANSLATION)
		target_link_libraries(${NAME} Threads::Threads)
	endif()

	if (SANITIZE)
		target_link_libraries(${NAME} "-fsanitize=address,undefined")
	elseif (TSAN)
		target_link_libraries(${NAME} "-fsanitize=thread,undefined")
	endif()
	if (GPROF)
		target_link_libraries(${NAME} "-pg")
	endif()

endfunction()

add_emulator(rvlinux  EMULATOR_MODE_LINUX=1)
if (NEWLIB_EMULATOR)
	add_emulator(rvnewlib EMULATOR_MODE_NEWLIB=1)
endif()
if (MICRO_EMULATOR)
	add_emulator(rvmicro  EMULATOR_MODE_MICRO=1)
endif()
